using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;

namespace Framework
{
    public class ByteBuffer
    {
        public int Length
        {
            get
            {
                return this._buffer.Length;
            }
        }
        public int ResizeValue;
        public byte[] Buffer { get { return _buffer; } }
        private byte[] _buffer;
        public ByteBuffer(int size) : this(new byte[size])
        {
        }
        public ByteBuffer(byte[] buffer)
        {
            _buffer = buffer;
            ResizeValue = 1024;
        }
        public void Dispose()
        {
            _buffer = null;
        }
        public void CopyTo(Array dst, int count = 0, int dstOffset = 0, int srcOffset = 0)
        {
            if (dstOffset < 1)
            {
                dstOffset = 0;
            }
            if (srcOffset < 1)
            {
                srcOffset = 0;
            }
            var srcCount = this._buffer.Length - srcOffset;
            var dstCount = dst.Length - dstOffset;
            if (count < 1)
            {

                count = dstCount < srcCount ? dstCount : srcCount;
            }
            else
            {
                if (count > srcCount || count > dstCount)
                {
                    throw new ArgumentOutOfRangeException();
                }
            }
            System.Buffer.BlockCopy(_buffer, srcOffset, dst, dstOffset, count);
        }
        public void SetSize(int value)
        {
            var len = value - _buffer.Length;
            if (len > 0)
            {
                if (ResizeValue < 0)
                {
                    ResizeValue = 0;
                }
                byte[] array = new byte[value + ResizeValue];
                System.Buffer.BlockCopy(_buffer, 0, array, 0, _buffer.Length);
                this._buffer = array;
            }
        }
        protected void WriteLittleEndian(int offset, int count, ulong data)
        {
            if (BitConverter.IsLittleEndian)
            {
                for (int i = 0; i < count; i++)
                {
                    this._buffer[offset + i] = (byte)(data >> i * 8);
                }
                return;
            }
            for (int j = 0; j < count; j++)
            {
                this._buffer[offset + count - 1 - j] = (byte)(data >> j * 8);
            }
        }
        protected ulong ReadLittleEndian(int offset, int count)
        {
            // this.AssertOffsetAndLength(offset, count);
            ulong num = 0UL;
            if (BitConverter.IsLittleEndian)
            {
                for (int i = 0; i < count; i++)
                {
                    num |= (ulong)_buffer[offset + i] << i * 8;
                }
            }
            else
            {
                for (int j = 0; j < count; j++)
                {
                    num |= (ulong)_buffer[offset + count - 1 - j] << j * 8;
                }
            }
            return num;
        }
        private int AssertOffsetAndLength(int offset, int length)
        {
            if (offset < 0 || offset > this._buffer.Length - length)
            {
                throw new ArgumentOutOfRangeException(string.Format("offset:{0},length:{1},frame:{2}", offset, length, UnityEngine.Time.frameCount));
            }
            return length;
        }
        public int Write(int offset, sbyte value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 1);
            this._buffer[offset] = (byte)value;
            return writeCount;
        }
        public int Write(int offset, byte value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 1);
            this._buffer[offset] = value;
            return writeCount;
        }
        public int Write(int offset, byte value, int count)
        {
            var writeCount = this.AssertOffsetAndLength(offset, count);
            for (int i = 0; i < count; i++)
            {
                this._buffer[offset + i] = value;
            }
            return writeCount;
        }
        public int Write(int offset, string value)
        {
            var count = Encoding.UTF8.GetByteCount(value);
            _ = this.AssertOffsetAndLength(offset, count);
            Encoding.UTF8.GetBytes(value, 0, value.Length, this._buffer, offset);
            return count;
        }
        public int Write(int offset, short value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 2);
            this.WriteLittleEndian(offset, 2, (ulong)((long)value));
            return writeCount;
        }
        public int Write(int offset, ushort value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 2);
            this.WriteLittleEndian(offset, 2, (ulong)value);
            return writeCount;
        }
        public int Write(int offset, int value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 4);
            this.WriteLittleEndian(offset, 4, (ulong)((long)value));
            return writeCount;
        }
        public int Write(int offset, uint value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 4);
            this.WriteLittleEndian(offset, 4, (ulong)value);
            return writeCount;
        }
        public int Write(int offset, long value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 8);
            this.WriteLittleEndian(offset, 8, (ulong)value);
            return writeCount;
        }
        public int Write(int offset, ulong value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 8);
            this.WriteLittleEndian(offset, 8, value);
            return writeCount;
        }
        public int Write(int offset, float value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 4);
            ByteBuffer.ConversionUnion conversionUnion;
            conversionUnion.intValue = 0;
            conversionUnion.floatValue = value;
            this.WriteLittleEndian(offset, 4, (ulong)((long)conversionUnion.intValue));
            return writeCount;
        }
        public int Write(int offset, double value)
        {
            var writeCount = this.AssertOffsetAndLength(offset, 8);
            this.WriteLittleEndian(offset, 8, (ulong)BitConverter.DoubleToInt64Bits(value));
            return writeCount;
        }
        public int Write<T>(int dstOffset, T[] src, int srcOffset = 0, int count = 0) where T : struct
        {
            if (src == null)
            {
                throw new ArgumentNullException("Cannot Write a null array");
            }
            if (src.Length == 0)
            {
                throw new ArgumentException("Cannot Write an empty array");
            }
            int elememtCount = src.Length - srcOffset;
            if (count < 1)
            {
                count = elememtCount;
            }
            else
            {
                if (count > elememtCount)
                {
                    throw new ArgumentException("Count bigger than Length-srcOffset");
                }
            }
            if (!ByteBuffer.IsSupportedType<T>())
            {
                string str = "Cannot Write an array of type ";
                Type typeFromHandle = typeof(T);
                throw new ArgumentException(str + (typeFromHandle?.ToString()) + " into this buffer");
            }
            if (BitConverter.IsLittleEndian)
            {
                int num = ByteBuffer.SizeOf<T>() * count;
                var writeCount = this.AssertOffsetAndLength(dstOffset, num);
                System.Buffer.BlockCopy(src, srcOffset, this._buffer, dstOffset, num);
                return writeCount;
            }
            throw new NotImplementedException("Big Endian Support not implemented yet for Writeing typed arrays");
        }
        //读

        public sbyte ReadSbyte(int index)
        {
            this.AssertOffsetAndLength(index, 1);
            return (sbyte)this._buffer[index];
        }
        public byte Read(int index)
        {
            this.AssertOffsetAndLength(index, 1);
            return this._buffer[index];
        }
        public string ReadStringUTF8(int startPos, int len)
        {
            return Encoding.UTF8.GetString(this._buffer, startPos, len);
        }
        public short ReadShort(int index)
        {
            return (short)this.ReadLittleEndian(index, 2);
        }
        public ushort ReadUshort(int index)
        {
            return (ushort)this.ReadLittleEndian(index, 2);
        }
        public int ReadInt(int index)
        {
            return (int)this.ReadLittleEndian(index, 4);
        }
        public uint ReadUint(int index)
        {
            return (uint)this.ReadLittleEndian(index, 4);
        }
        public long ReadLong(int index)
        {
            return (long)this.ReadLittleEndian(index, 8);
        }
        public ulong ReadUlong(int index)
        {
            return this.ReadLittleEndian(index, 8);
        }
        public float ReadFloat(int index)
        {
            var result = (int)this.ReadLittleEndian(index, 4);
            ByteBuffer.ConversionUnion conversionUnion;
            conversionUnion.floatValue = 0f;
            conversionUnion.intValue = result;
            return conversionUnion.floatValue;
        }
        public double ReadDouble(int index)
        {
            return BitConverter.Int64BitsToDouble((long)this.ReadLittleEndian(index, 8));
        }
        public static int SizeOf<T>()
        {
            return ByteBuffer.genericSizes[typeof(T)];
        }
        public static bool IsSupportedType<T>()
        {
            return ByteBuffer.genericSizes.ContainsKey(typeof(T));
        }
        private static readonly Dictionary<Type, int> genericSizes = new Dictionary<Type, int>()
        {
            {
                typeof(bool),
                1
            },
            {
                typeof(float),
                4
            },
            {
                typeof(double),
                8
            },
            {
                typeof(sbyte),
                1
            },
            {
                typeof(byte),
                1
            },
            {
                typeof(short),
                2
            },
            {
                typeof(ushort),
                2
            },
            {
                typeof(int),
                4
            },
            {
                typeof(uint),
                4
            },
            {
                typeof(ulong),
                8
            },
            {
                typeof(long),
                8
            }
        };
        [StructLayout(LayoutKind.Explicit)]
        private struct ConversionUnion
        {
            [FieldOffset(0)]
            public int intValue;
            [FieldOffset(0)]
            public float floatValue;
        }
    }
}